#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <ctime>
#include <sys/types.h>
#include <unistd.h>
#include <cstdarg>
#include <pthread.h>
#include <fstream>
using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

bool gIsSave = false; // gIsSave标志位,false表示将日志消息写入到显示器,true表示将日志消息写入到文件
const string logname = "./log.txt";

enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL,
};
string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "Debug";
    case INFO:
        return "Info";
    case WARNING:
        return "Warning";
    case ERROR:
        return "Error";
    case FATAL:
        return "Fatal";
    default:
        return "Unknown Level";
    }
}
string GetTimeStr()
{
    time_t curtime = time(nullptr);
    struct tm *formtime = localtime(&curtime);
    if (formtime == nullptr)
    {
        return "unknown time";
    }
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d",
             formtime->tm_year + 1900, formtime->tm_mon + 1, formtime->tm_mday,
             formtime->tm_hour, formtime->tm_min, formtime->tm_sec);

    return buffer;
}

void SaveFile(const string filename, const string &message)
{
    ofstream out(filename, ios::app);//追加方式打开文件
    if (!out.is_open())
    {
        return;
    }
    out << message;
    out.close();
}

void LogMessage(string filename, int line, bool issave, int level, const char *format, ...)
{
    string levelstr = LevelToString(level);
    string timestr = GetTimeStr();
    pid_t selfpid = getpid();
    // cout<<"levestr:"<<levelstr<<" timestr:"<<timestr<<" pid:"<<selfpid<<endl;
    // cout<<"levestr:"<<levelstr<<" timestr:"<<timestr<<" pid:"<<selfpid<<" filename:"<<filename<<" line:"<<line<<endl;

    va_list arg;
    va_start(arg, format);
    char buffer[1024];
    vsnprintf(buffer, sizeof(buffer), format, arg);
    // cout<<"levestr:"<<levelstr<<" timestr:"<<timestr<<" pid:"<<selfpid<<" filename:"<<filename<<" line:"<<line<<" Log Message:"<<buffer<<endl;
    

    // 日志既可以输出到显示器也可以写入文件中
    // 调用LogMessage()的执行流可以是任意多个,多个执行流向显示器写入
    // 显示器为临界资源,需要加锁保护
    // 当多个执行流向文件写入,文件则为临界资源,需要加锁保护
    pthread_mutex_lock(&mutex);
    if (!issave)
    {
        cout << "[" << timestr << "]"
             << " [" << levelstr << "]"
             << " [pid:" << selfpid << "]"
             << " [" << filename << "]"
             << " [" << line << "]"
             << " [message:" << buffer << "]" << endl;
    }
    else
    {
        string message = "[" + timestr + "]" + "[" + levelstr + "]" +
                         "[" + to_string(selfpid) + "]" +
                         "[" + filename + "]" + "[" + to_string(line) + "] " + buffer + "\n";
        SaveFile(logname, message);
    }
    pthread_mutex_unlock(&mutex);
}

#define LOG(level, format, ...)                                                \
    do                                                                         \
    {                                                                          \
        LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__); \
    } while (0)

#define EnableFile() do{ gIsSave=true; } while(0) //日志消息输出到文件
#define EnableScreen() do{ gIsSave=false;} while(0) //日志消息输出到显示器
